package cn.js189.uqc.common;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.js189.common.constants.ErrorConstants;
import cn.js189.common.constants.InvoiceConstant;
import cn.js189.common.domain.ReqInfo;
import cn.js189.common.domain.RespHead;
import cn.js189.common.domain.RespInfo;
import cn.js189.common.util.Utils;
import cn.js189.common.util.exception.BaseAppException;
import cn.js189.common.util.exception.ExceptionUtil;
import cn.js189.common.util.helper.DateHelper;
import cn.js189.common.util.helper.UUIDHelper;
import cn.js189.uqc.domain.LogOperation;
import cn.js189.uqc.domain.User;
import cn.js189.uqc.domain.invoice.*;
import cn.js189.uqc.mapper.CheckInfoMapper;
import cn.js189.uqc.redis.RedisOperation;
import cn.js189.uqc.service.InvoiceService;
import cn.js189.uqc.task.QuartzPdfUrl;
import cn.js189.uqc.util.*;
import cn.js189.uqc.util.thread.LogOperationThread;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@SuppressWarnings("restriction")
@Component
public class QXInvoiceService{

    public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
    public static final String UUID2 = getUUID();
    public static final String PLAIN_TEXT = "apiname=com.alipay.account.auth&app_id=2019071865816965&app_name=mc&auth_type=AUTHACCOUNT&biz_type=openservice&method=alipay.open.auth.sdk.code.get&pid=2088701509444048&product_id=APP_FAST_LOGIN&scope=kuaijie&sign_type=RSA2&target_id="+UUID2;

	private static final Logger logger = LoggerFactory.getLogger(QXInvoiceService.class);

    @Resource
    public InvoiceService invoiceService;

	@Resource
	private CheckInfoMapper checkInfoMapper;

	@Resource
	private RedisOperation redisOperation;


	private  boolean issueInvoiceJSCheck(String areaCode, JSONObject body){
        if(CharSequenceUtil.isBlank(areaCode)){
        	return false;
        }

        JSONObject orderInfo = body.getJSONObject("orderInfo");
        JSONArray detailList = orderInfo.getJSONArray("orderDetail");
        if(orderInfo == null || orderInfo.isEmpty() ||  detailList == null || detailList.isEmpty()){
        	return false;
        }

        JSONObject userInfo = body.getJSONObject("userInfo");
        if(userInfo == null){
        	return false;
        }

        if(null == orderInfo.getString("phone") || null == orderInfo.getString("type") || null == orderInfo.getString("titleType") || null == orderInfo.getString("titleName")
        		|| null == orderInfo.getString("money") || null == orderInfo.getString("phoneType") || null == orderInfo.getString("mail")
        		|| null == orderInfo.getString("source")){
        	return false;
        }
        if(orderInfo.getString("phone").isEmpty() || orderInfo.getString("type").isEmpty() || orderInfo.getString("titleType").isEmpty() ||
        		orderInfo.getString("money").isEmpty() || orderInfo.getString("phoneType").isEmpty() || orderInfo.getString("mail").isEmpty()){
        	return false;
        }

        return true;
	}

	private boolean invoiceJSCheck1000(List<Map<String, String>> lsMap, JSONObject obj, InvoiceOrderDetail invoice) throws BaseAppException {
		if(obj.getString("billMonth") != null && !obj.getString("billMonth").isEmpty()){
			if(lsMap != null && !lsMap.isEmpty()){
				for(Map<String, String> map : lsMap){
					if(map.get("productId") != null && map.get("productId").equals(obj.getString("productId"))
							&& map.get("billMonth") != null && map.get("billMonth").equals(obj.getString("billMonth"))){
						ExceptionUtil.throwBusiExceptionMsg(ErrorConstants.PARAM_EXISTS_INVOICEFAIL,"发票已存在");
					}
				}
			}
		}
		else{
			ExceptionUtil.throwBusiExceptionMsg(ErrorConstants.PARAM_EMPTY_EXIST_INVOICEFAIL,"参数为空或者记录已存在");
		}

		invoice.setBillMonth(obj.getString("billMonth"));
    	return true;
	}

	private boolean invoiceJSCheck1100(List<Map<String, String>> lsMap, JSONObject obj, InvoiceOrderDetail invoice) throws BaseAppException {
   		if(obj.getString("paymentDate") != null && obj.getString("paymentDate").trim().length() > 0 &&
   		       obj.getString("id") != null && obj.getString("id").trim().length() > 0){

            if (lsMap != null && !lsMap.isEmpty()) {
                for (Map<String, String> map : lsMap) {
	                if (map.get("productId") != null && map.get("productId").equals(obj.getString("productId"))
			                && map.get("billId") != null && map.get("billId").equals(obj.getString("billId"))) {
						ExceptionUtil.throwBusiExceptionMsg(ErrorConstants.PARAM_EXISTS_INVOICEFAIL,"发票已存在");
	                }
                }
            }
		}
		else{
			ExceptionUtil.throwBusiExceptionMsg(ErrorConstants.PARAM_EMPTY_EXIST_INVOICEFAIL,"参数为空或者记录已存在");
		}

		invoice.setPaymentDate(obj.getString("paymentDate"));
		invoice.setBillId(obj.getString("id"));
   		return true;
	}


	private boolean invoiceJSCheck1200(List<Map<String, String>> lsMap, JSONObject obj, InvoiceOrderDetail invoice) throws BaseAppException {
   		if(obj.getString("orderDate") != null && obj.getString("orderDate").trim().length() > 0 &&
    	       obj.getString("orderNumber") != null && obj.getString("orderNumber").trim().length() > 0){

   			if(lsMap != null && !lsMap.isEmpty()){
				for(Map<String, String> map : lsMap){
					if(map.get("productId") != null && map.get("productId").equals(obj.getString("productId"))
							&& map.get("orderNumber") != null && map.get("orderNumber").equals(obj.getString("orderNumber"))){
						ExceptionUtil.throwBusiExceptionMsg(ErrorConstants.PARAM_EXISTS_INVOICEFAIL,"发票已存在");
					}
				}
			}
 		}
 		else{
			ExceptionUtil.throwBusiExceptionMsg(ErrorConstants.PARAM_EMPTY_EXIST_INVOICEFAIL,"参数为空或者记录已存在");
 		}

		invoice.setOrderDate(obj.getString("orderDate"));
    	invoice.setOrderNumber(obj.getString("orderNumber"));
   		return true;
	}



	private InvoiceOrderDetail setInvoice(String type, List<Map<String, String>> lsMap, JSONObject obj, String partyId, String issueId, String wxAuth, String aliAuth) throws BaseAppException {
		InvoiceOrderDetail invoice = new InvoiceOrderDetail();
    	invoice.setId(UUIDHelper.getUUID());
    	invoice.setOrderId(issueId);
    	invoice.setBillId(null == obj.getString("id") ? "" : obj.getString("id"));
    	invoice.setProductId(obj.getString("productId"));
    	invoice.setMoney(obj.getString("amount"));
    	invoice.setInvoiceId("");
    	invoice.setStatus(InvoiceConstant.INVOICE_NOT);
    	invoice.setType(type);
    	invoice.setPdfUrl("");
    	invoice.setPdfDownload(InvoiceConstant.PDF_EMPTY);
    	invoice.setPartyId(partyId);
    	invoice.setDescription("");
		invoice.setGmtCreate(new Date());

    	if(null != wxAuth){
    		invoice.setWxAuth(wxAuth);
    	}

    	if(null != aliAuth){
    		invoice.setAliAuth(aliAuth);
    	}

   		switch (type) {
			case "1000":
				invoiceJSCheck1000(lsMap, obj, invoice);
				break;
			case "1100":
				invoiceJSCheck1100(lsMap, obj, invoice);
           		break;
			case "1200":
				invoiceJSCheck1200(lsMap, obj, invoice);
                break;
			default:
				break;
		}
   		return invoice;
	}

	private Map<String, Object> getMap(JSONObject orderInfo, String issueId, String areaCode, List<InvoiceOrderDetail> invoiceList) {
		Map<String, Object> map = new HashMap<>();
		map.put("invoiceId", "");
		map.put("issueId", issueId);
		EInvoiceUtil.setHeadMap(areaCode);
		map.put("lanId", StaticDataMappingUtil.getRegionByAreaCode(areaCode));
		
		String type = orderInfo.getString("type");
		String phone = orderInfo.getString("phone");
		String partyType = orderInfo.getString("titleType");
		String taxNum = orderInfo.getString("taxNum");
		String isMerge = orderInfo.getString("isMerge");

		List<Map<String, String>> objects = new ArrayList<>();
		for (InvoiceOrderDetail item : invoiceList) {
			Map<String, String> m = new HashMap<>();
			if (type.trim().equals("1000")) {
				m.put("billMonth", item.getBillMonth());
			} else if (type.trim().equals("1100")) {
				m.put("paymentDate", item.getPaymentDate());
			} else {
				m.put("orderDate", item.getOrderDate());
				m.put("orderNumber", item.getOrderNumber());
			}

			m.put("id", null == item.getBillId() ? "" : item.getBillId());
			m.put("amount", item.getMoney());
			m.put("productId", item.getProductId());
			m.put("invoiceId", "");
			m.put("status", InvoiceConstant.INVOICE_NOT);
			objects.add(m);
		}

		// 政企传购买方纳税人识别号,开户行+银行账号,企业地址,备注说明
		if (StringUtils.equals("2", partyType)) {
			String bankName = orderInfo.getString("bankName");
			String bankAccount = orderInfo.getString("bankAccount");
			String companyAddr = orderInfo.getString("companyAddr");
			String companyPhone = orderInfo.getString("companyPhone");
			if (StringUtils.isNotEmpty(taxNum)){
				map.put("buyerTaxPayerId", taxNum);
			}
			if (StringUtils.isNotEmpty(bankName)&&StringUtils.isNotEmpty(bankAccount)) {
				map.put("bankAccount", bankName + " " + bankAccount);
			}
			if(StringUtils.isNotEmpty(companyAddr)){
				map.put("buyerAddress", companyAddr);
			}
			if(StringUtils.isNotEmpty(companyPhone)){
				map.put("buyerPhone", companyPhone);
			}
		}

		map.put("invoiceHeader",orderInfo.getString("titleName"));
		map.put("printAccNbr", phone);
		map.put("objects", objects);
		map.put("remoteSource", EInvoiceUtil.REMOTE_SOURCE);
		map.put("remotepass", EInvoiceUtil.getRemotePass());
		map.put("type", type);
		map.put("isMerge", isMerge);
		
		return map;
	}

	private InvoiceOrder setOrder(String issueId, String type, String areaCode, JSONObject orderInfo, String loginAccNbr) {
		//订单主体
		InvoiceOrder order = new InvoiceOrder();
		order.setOrderId(issueId);
		order.setPhone(orderInfo.getString("phone"));
		order.setType(type);
		order.setTitleType(orderInfo.getString("titleType"));
		order.setTitleName(orderInfo.getString("titleName"));
		order.setPhoneType(orderInfo.getString("phoneType"));
		order.setMoney(orderInfo.getString("money"));
		order.setMail(orderInfo.getString("mail"));
		order.setAreaCode(areaCode);
		order.setSource(orderInfo.getString("source"));
		order.setLoginAccNbr(loginAccNbr);
		order.setIsMerge(orderInfo.getString("isMerge"));

		//政企
		if (StringUtils.equals("2", orderInfo.getString("titleType"))) {
			order.setTaxNum(orderInfo.getString("taxNum"));
			order.setBankName(orderInfo.getString("bankName"));
			order.setBankAccount(orderInfo.getString("bankAccount"));
			order.setCompanyAddr(orderInfo.getString("companyAddr"));
			order.setCompanyPhone(orderInfo.getString("companyPhone"));
			order.setRemark(orderInfo.getString("remark"));
		}
		return order;
	}

	private User setUser(JSONObject userInfo, String partyId, String areaCode, String partyType, String taxNum) {
		User user = new User();
		user.setIdCard(null == userInfo.getString("idCard") ? "" : userInfo.getString("idCard"));
		user.setPartyId(partyId);
		user.setLoginPhone(null == userInfo.getString("loginPhone") ? "" : userInfo.getString("loginPhone"));
		user.setPhoneType(null == userInfo.getString("phoneType") ? "" : userInfo.getString("phoneType"));
		user.setAreaCode(areaCode);
		user.setMail(null == userInfo.getString("mail") ? "" : userInfo.getString("mail"));
		user.setPartyType(partyType);
		user.setTaxNum(taxNum);
		return user;
	}

	//开具发票
    public synchronized String issueInvoiceJS(String reqParams) {
        logger.info("开具发票 input :{}", reqParams);
	    RespInfo responseJson = new RespInfo();
	    RespHead responseHead = new RespHead();
        responseJson.setHead(responseHead);
        String tranId = "";
        JSONObject response;
		final String issueId = "oid_" + UUIDHelper.getUUID();
        try {
            ReqInfo reqInfo = BeanUtil.toBean(reqParams, ReqInfo.class);
            String areaCode = reqInfo.getHead().getAreaCode();
            JSONObject body = reqInfo.getBody();
            String resourceAddr = body.getString("resourceAddr");
            JSONObject orderInfo = body.getJSONObject("orderInfo");
            
            boolean containAddr = EInvoiceUtil.containResourceAddr(resourceAddr);
            if (!containAddr) {
                return Utils.dealExceptionRespWithMsgAndStatus(responseHead, tranId, "请求资源地址不存在", "98", responseJson).toString();
            }
            if(!issueInvoiceJSCheck(areaCode, body)){
            	ExceptionUtil.throwBusiException(ErrorConstants.PARAM_EMPTY_INVOICEDETAIL);
            }
            final String type = orderInfo.getString("type");
            if(((!"1000".equals(type)) && (!"1100".equals(type)) && (!"1200".equals(type)))){
            	ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_TYPE_ERROR);
            }

            JSONArray detailList = orderInfo.getJSONArray("orderDetail");
	        String loginAccNbr = body.getString("loginAccNbr"); // 登陆号码
	        InvoiceOrder order = setOrder(issueId, type, areaCode, orderInfo, loginAccNbr);

            JSONObject userInfo = body.getJSONObject("userInfo");
            //开票类型 1000月结  1100充值实缴  1200营业业务
            //#开票详情表 partyId字段取值：充值1100和月结1000用acctId，如果是1200业务办理则是partyId
            //acctId为15050接口的callQurUserInfos4SmartBss accoutInfo的acctId
            String partyId = userInfo.getString("partyId");
            String idCard = userInfo.getString("idCard");
            if(CharSequenceUtil.isBlank(partyId) || CharSequenceUtil.isBlank(idCard)){
            	ExceptionUtil.throwBusiException(ErrorConstants.PARAM_EMPTY_USERTAIL);
            }

            String wxAuth = null;
            String aliAuth = null;
            Map<String,String> status = checkInfoMapper.selectWxAliAuthStatus(partyId);
            if(null != status && !status.isEmpty()){
            	wxAuth = status.get("wxAuth");
            	aliAuth = status.get("aliAuth");
            }

            List<Map<String, String>> lsMap = invoiceService.selectListByPartyId(partyId);
            List<InvoiceOrderDetail> invoiceList = new ArrayList<>();
            //订单发票列表
            for(int i=0;i<detailList.size();i++){
            	JSONObject obj = detailList.getJSONObject(i);
            	if(null == obj.getString("productId") || null == obj.getString("amount") || (obj.getString("productId").isEmpty() || obj.getString("amount").isEmpty())){
                	ExceptionUtil.throwBusiException(ErrorConstants.PARAM_EMPTY_INVOICEDETAIL);
                }

            	InvoiceOrderDetail invoice = setInvoice(type, lsMap, obj, partyId, issueId, wxAuth, aliAuth);
            	if(null != invoice){
            		invoiceList.add(invoice);
            	}else{
            		ExceptionUtil.throwBusiException(ErrorConstants.PARAM_EMPTY_EXIST_INVOICEFAIL);
            	}
            }
	        String partyType = orderInfo.getString("titleType");
			String taxNum = orderInfo.getString("taxNum");

			// 政企税号参数必填校验
			if (StringUtils.equals("2", partyType) && (StringUtils.isEmpty(taxNum) || StringUtils.isBlank(taxNum))) {
				ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_TAX_NUM_EMPTY);
			}
			logger.info("invoiceList={}", JSON.toJSON(invoiceList));
            //用户信息
			User user = setUser(userInfo, partyId, areaCode, partyType, taxNum);

            //封装企信参数
			Map<String, Object> map = getMap(orderInfo, issueId, areaCode, invoiceList);
	        logger.info("partyId={} orderId={}", partyId,issueId);

            JSONObject param = (JSONObject)JSON.toJSON(map);
            String url = EInvoiceUtil.getPostUrl(resourceAddr);
        	logger.debug("参数组装成功,开始执行MySql");

        	// 加锁 同一partyId同时提交一次
	        String lockValue = UUIDHelper.getUUID();
	        String lockKey = InvoiceConstant.USER_SUBMIT_INVOICE_ + partyId;
	        String lockResult = RedisUtil.tryLock(redisOperation, "提交开票申请", lockKey, lockValue);

	        if (!StringUtils.equals(lockResult, RedisUtil.LOCK_SUCCESS)) {
		        ExceptionUtil.throwBusiException(ErrorConstants.SUBMIT_INVOICE_ERROR);
	        }
	        
	        logger.info("开始同步历史列表中开具完成的发票信息...");
			boolean f = invoiceService.commitInvoiceInfo(order, invoiceList, user, "开具发票");
            if(!f){
				checkInfoMapper.deleteOrderByOrderId(issueId);
				checkInfoMapper.deleteOrderDetailByOrderId(issueId);
	            logger.error("MySql记录添加异常：{}", issueId);
            	ExceptionUtil.throwBusiException(ErrorConstants.ORDER_INSERT_ERROR);
            }

	        logger.debug("MySql发票记录添加成功: {}", issueId);
            logger.info("参数组装成功,开始调用企信接口URL:{}", url);
	        logger.info("企信开具发票接口入参:{}", param);

            String res = HttpClientUtils.sendPostWithHead(url, param.toString(), false, EInvoiceUtil.getInvoiceHeadMap());
            logger.info("企信开具发票接口出参:{}", res);

            if (!StringUtils.isEmpty(res)) {
                JSONObject resBody = JSON.parseObject(res);
                String result = resBody.getString("result");
                if("0".equals(result)){
                	invoiceService.updateInvoiceDetailStatusByOrderId(issueId, InvoiceConstant.INVOICE_ING);
                	insertData(areaCode, orderInfo.getString("phone"), partyId, "00", param.toString(), res);
                	response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, resBody, "success", "00", responseJson);
                } else{
					invoiceService.initQxError();
                	String msg = EInvoiceUtil.getErrmsg(result);
    				checkInfoMapper.deleteOrderByOrderId(issueId);
    				checkInfoMapper.deleteOrderDetailByOrderId(issueId);
            		insertData(areaCode, orderInfo.getString("phone"), partyId, "02", param.toString(), res);
	                response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, null, StringUtils.isEmpty(msg) ? "发票开具失败" : msg, StringUtils.isEmpty(result) ? ErrorConstants.ISSUE_INVOICE_FAIL : result, responseJson);
                }
            } else {
				checkInfoMapper.deleteOrderByOrderId(issueId);
				checkInfoMapper.deleteOrderDetailByOrderId(issueId);
            	insertData(areaCode, orderInfo.getString("phone"), partyId, "03", param.toString(), "");
                response = Utils.dealExceptionRespWithMsgAndStatus(responseHead, tranId, "企信接口调用失败", "97", responseJson);
            }
	        RedisUtil.unlock(redisOperation, "提交开票申请", lockKey, lockValue);
            return response.toString();
        }catch (BaseAppException e) {
			checkInfoMapper.deleteOrderByOrderId(issueId);
			checkInfoMapper.deleteOrderDetailByOrderId(issueId);
        	response = Utils.dealBaseAppExceptionResp(responseHead, tranId, responseJson, e.getMsg(), e.getCode());
            logger.error("(开具发票) BaseAppException :{}", e.getMessage());
            return response.toString();
        }catch (Exception e) {
			checkInfoMapper.deleteOrderByOrderId(issueId);
			checkInfoMapper.deleteOrderDetailByOrderId(issueId);
            response = Utils.dealExceptionResp(responseHead, tranId, responseJson);
            logger.error("(开具发票) Exception :{}", e.getMessage());
            return response.toString();
        }
    }
    
	private void insertData(String areaCode, String accNbr, String phone, String returnCode, String inputParam, String outParam){
      	 ExecutorService executorService = Executors.newSingleThreadExecutor();
      	 InnerApiLog innerApiLog = new InnerApiLog("dzfp", areaCode, accNbr, phone, "18",
      			returnCode, inputParam, outParam, "0");
      	 InnerApiLogThread innerApiLogThread = new InnerApiLogThread(innerApiLog);
      	 executorService.execute(innerApiLogThread);
      	 executorService.shutdown();
    }

	//历史查询
	public String hisInvoiceInfoJS(String reqParams) {
        logger.debug("查询开票历史 input :{}", reqParams);
		RespInfo responseJson = new RespInfo();
		RespHead responseHead = new RespHead();
        String tranId = "";
        JSONObject response;
        try {
            ReqInfo reqInfo = BeanUtil.toBean(reqParams, ReqInfo.class);
            JSONObject body = reqInfo.getBody();
            String idCard = body.getString("idCard");
            String beginTime = body.getString("beginTime");
            String endTime = body.getString("endTime");

            if(null == idCard || null == beginTime || null == endTime ||
            		idCard.isEmpty() || beginTime.isEmpty() || endTime.isEmpty()){
    			ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_PARAM_EMPTY);
    		}

            List<InvoiceHisDto> list = checkInfoMapper.selectInvoiceHis(beginTime, endTime, idCard);
        	JSONObject resObj = new JSONObject();
        	resObj.put("items", list);
            response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, resObj, "success", "00", responseJson);
            return response.toString();
        } catch (BaseAppException e) {
            response = Utils.dealBaseAppExceptionResp(responseHead, tranId, responseJson);
            logger.debug("(发票历史查询)BaseAppException :{}", e.getMessage());
            return response.toString();
        } catch (Exception e) {
            response = Utils.dealExceptionResp(responseHead, tranId, responseJson);
            logger.debug("(发票历史查询) Exception :{}", e.getMessage());
            return response.toString();
        }
    }

    //发票信息查询
    public String invoiceJS(String reqParams) {
        logger.debug("发票信息查询 input :{}", reqParams);
	    RespInfo responseJson = new RespInfo();
	    RespHead responseHead = new RespHead();
        String tranId = "";
        JSONObject response;

        try {
            ReqInfo reqInfo = BeanUtil.toBean(reqParams, ReqInfo.class);
            JSONObject body = reqInfo.getBody();

            String did = body.getString("did");
    		if(null == did || did.isEmpty()){
    			ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_PARAM_EMPTY);
    		}

            InvoiceDetail detail = checkInfoMapper.selectInvoiceType(did);

        	JSONObject resObj = new JSONObject();
        	resObj.put("item", detail);
            response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, resObj, "success", "00", responseJson);
            return response.toString();
        } catch (BaseAppException e) {
            response = Utils.dealBaseAppExceptionResp(responseHead, tranId, responseJson);
            logger.debug("(发票信息查询)BaseAppException :{}", e.getMessage());
            return response.toString();
        } catch (Exception e) {
            response = Utils.dealExceptionResp(responseHead, tranId, responseJson);
            logger.debug("(发票信息查询) Exception :{}", e.getMessage());
            return response.toString();
        }
    }
    
    public String invoiceJSByOrderId(String reqParams) {
        logger.debug("invoiceJSByOrderId input :{}", reqParams);
	    RespInfo responseJson = new RespInfo();
	    RespHead responseHead = new RespHead();
        String tranId = "";
        JSONObject response;

        try {
            ReqInfo reqInfo = BeanUtil.toBean(reqParams, ReqInfo.class);
            JSONObject body = reqInfo.getBody();

            String orderId = body.getString("orderId");
    		if(null == orderId || orderId.isEmpty()){
    			ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_PARAM_EMPTY);
    		}
            List<InvoiceDetail> listDetail = checkInfoMapper.selectInvoiceTypeByOrderId(orderId);

        	JSONObject resObj = new JSONObject();
        	resObj.put("item", listDetail);
            response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, resObj, "success", "00", responseJson);
            return response.toString();
        } catch (BaseAppException e) {
            response = Utils.dealBaseAppExceptionResp(responseHead, tranId, responseJson);
            logger.debug("invoiceJSByOrderId BaseAppException :{}", e.getMessage());
            return response.toString();
        } catch (Exception e) {
            response = Utils.dealExceptionResp(responseHead, tranId, responseJson);
            logger.debug("invoiceJSByOrderId Exception :{}", e.getMessage());
            return response.toString();
        }
    }


    private static String getUUID() {
        return IdUtil.simpleUUID().toUpperCase().replace("-", "");
    }

	public String getSign() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
		RespInfo responseJson = new RespInfo();
		RespHead responseHead = new RespHead();
    	String tranId = "";
    	JSONObject response;
    	byte[] keyBytes;
        keyBytes = (new BASE64Decoder()).decodeBuffer(InvoiceConstant.APP_PRIVATE_KEY);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(PLAIN_TEXT.getBytes(StandardCharsets.UTF_8));
        byte[] result = signature.sign();
        BASE64Encoder base64Encoder = new BASE64Encoder();
        String res =base64Encoder.encode(result);
        res = URLEncoder.encode(res,"utf-8");
        res=PLAIN_TEXT+"&sign="+res;

		JSONObject resObj = new JSONObject();
		resObj.put("status", "200");
		resObj.put("sign", res);
        response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, resObj, "success", "00",
				responseJson);
        return response.toString();
    }


	public String updateUid(String reqParams) {
		logger.debug("更新支付宝用户id input :{}", reqParams);
		RespInfo responseJson = new RespInfo();
		RespHead responseHead = new RespHead();
		String tranId = "";
		JSONObject response;

		try {
		    ReqInfo reqInfo = BeanUtil.toBean(reqParams, ReqInfo.class);
			JSONObject body = reqInfo.getBody();
			String uid = body.getString("uid");
			String partyId = body.getString("partyId");

            if(null == partyId || partyId.isEmpty()){
           	     ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_PARAM_EMPTY);
            }

            if(null == uid || uid.isEmpty()){
          	     ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_PARAM_EMPTY);
            }

    		User userInfo = this.checkInfoMapper.selectUserByPartyId(partyId);
    		if (null == userInfo) {
    			ExceptionUtil.throwBusiException(ErrorConstants.USER_NULL_BYPARTYID);
    		}

			int row = checkInfoMapper.updateAliUserInfo(uid, partyId);

			JSONObject resObj = new JSONObject();
			resObj.put("status", row > 0 ? "200" : "500");
			response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, resObj, "success", "00",
					responseJson);
			return response.toString();
		} catch (BaseAppException e) {
			response = Utils.dealBaseAppExceptionResp(responseHead, tranId, responseJson);
			logger.debug("(更新支付宝用户id)BaseAppException :{}", e.getMessage());
			return response.toString();
		} catch (Exception e) {
			response = Utils.dealExceptionResp(responseHead, tranId, responseJson);
			logger.debug("(更新支付宝用户id) Exception :{}", e.getMessage());
			return response.toString();
		}
	}

    public String selectWxAliAuthStatus(String reqParams){
    	 logger.debug("查询支付宝授权状态input :{}", reqParams);
	    RespInfo responseJson = new RespInfo();
	    RespHead responseHead = new RespHead();
         String tranId = "";
         JSONObject response;

         try {
             ReqInfo reqInfo = BeanUtil.toBean(reqParams, ReqInfo.class);
             JSONObject body = reqInfo.getBody();
             String partyId = body.getString("partyId");

             if(null == partyId || partyId.isEmpty()){
            	 ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_PARAM_EMPTY);
             }

	         // 加锁 同一partyId同时提交一次
	         String lockValue = UUIDHelper.getUUID();
	         String lockKey = InvoiceConstant.INIT_USER_INFO_PREFIX + partyId;
	         String lockResult = RedisUtil.waitForTryLock(redisOperation, "查询用户授权状态", lockKey, lockValue);

	         if (StringUtils.equals(lockResult, RedisUtil.LOCK_SUCCESS)) {

		         User userInfo = this.checkInfoMapper.selectUserByPartyId(partyId);

		         if (null == userInfo) {
		         	User user = new User();
		         	user.setPartyId(partyId);
		         	checkInfoMapper.insertUserInfos(user);
		         }
		         RedisUtil.unlock(redisOperation, "查询用户授权状态", lockKey, lockValue);

	         }

             Map<String,String> status = checkInfoMapper.selectWxAliAuthStatus(partyId);

         	 JSONObject resObj = new JSONObject();
         	 resObj.put("status", status);
             response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, resObj, "success", "00", responseJson);
             return response.toString();
         } catch (BaseAppException e) {
	         response = Utils.dealBaseAppExceptionResp(responseHead, tranId, responseJson, e.getMsg(), e.getCode());
             logger.debug("(查询支付宝授权状态)BaseAppException :{}", e.getMessage());
             return response.toString();
         } catch (Exception e) {
             response = Utils.dealExceptionResp(responseHead, tranId, responseJson);
             logger.debug("(查询支付宝授权状态) Exception :{}", e.getMessage());
             return response.toString();
         }
    }


	public String cancelStatus(String reqParams) {
		logger.debug("取消支付宝用户授权 input :{}", reqParams);
		RespInfo responseJson = new RespInfo();
		RespHead responseHead = new RespHead();
		responseJson.setHead(responseHead);
		String tranId = "";
		JSONObject response;

		try {
			ReqInfo reqInfo = BeanUtil.toBean(reqParams, ReqInfo.class);
			JSONObject body = reqInfo.getBody();
            String partyId = body.getString("partyId");

            if(null == partyId || partyId.isEmpty()){
           	     ExceptionUtil.throwBusiException(ErrorConstants.INVOICE_PARAM_EMPTY);
            }

    		User userInfo = this.checkInfoMapper.selectUserByPartyId(partyId);
    		if (null == userInfo) {
    			ExceptionUtil.throwBusiException(ErrorConstants.USER_NULL_BYPARTYID);
    		}

			int row = checkInfoMapper.updateAliStatus(partyId);

			JSONObject resObj = new JSONObject();
			resObj.put("status", row > 0 ? "200" : "500");
			response = Utils.dealSuccessRespWithMsgAndStatus(responseHead, tranId, resObj, "success", "00",
					responseJson);
			return response.toString();
		} catch (BaseAppException e) {
			response = Utils.dealBaseAppExceptionResp(responseHead, tranId, responseJson);
			logger.debug("(取消支付宝用户授权)BaseAppException :{}", e.getMessage());
			return response.toString();
		} catch (Exception e) {
			response = Utils.dealExceptionResp(responseHead, tranId, responseJson);
			logger.debug("(取消支付宝用户授权) Exception :{}", e.getMessage());
			return response.toString();
		}
	}

	public void viewPdf(HttpServletRequest request, HttpServletResponse response) {
		String id = request.getParameter("id");
		try{
			if(null == id || (id.isEmpty())){
				logger.info("Param id is null");
				ExceptionUtil.throwBusiException(ErrorConstants.DOWN_ID_NULL);
			}
			logger.info("id={}",id);
			id = UrlUtil.deCryptAndDecode(id);
			logger.info("解密后id={}", id);
			String[] idArr = id.split(",");
			String invoiceId = idArr[0];
			String invoiceCode = idArr[1];
			String invoiceNo = idArr[2];
			String url = checkInfoMapper.selectByInvoiceInfo(invoiceId,invoiceCode,invoiceNo);
			logger.info("url:{}", url);

			if(null == url || (url.isEmpty())){
				logger.info("pdfUrl is null: invoiceId={}", id);
				ExceptionUtil.throwBusiException(ErrorConstants.DOWN_URL_NULL);
			}

			String path = downLoadUpLoad(url);
			if(!path.isEmpty()){
				logger.info("下载成功,开始输出流");
				response.setContentType("application/pdf;charset=UTF-8");
				response.setContentType("application/octet-stream");
				OutputStream out;

				try(FileInputStream in = new FileInputStream(path)){
					out = response.getOutputStream();
					byte[] b = new byte[512];
					while((in.read(b)) != -1){
						out.write(b);
					}
					out.flush();
					out.close();
				}
				logger.info("文件输出成功");
			}
			else{
				logger.info("从url:{}  下载pdf失败", url);
			}
		}
		catch(Exception ex){
			logger.info(ex.getMessage());
		}

	}

	private String downLoadUpLoad(String pdfUrl) throws BaseAppException {
		if (StringUtils.isEmpty(pdfUrl)) {
			ExceptionUtil.throwBusiException(ErrorConstants.WX_INVOICE_PDFURL_EMPTY);
		}

		String fileName = DateHelper.getCurrentTimeStampString() + ".pdf";
		String uuid = UUIDHelper.getUUID() + "/";
		String savePath = Objects.requireNonNull(QuartzPdfUrl.class.getClassLoader().getResource("/")).getPath();
		savePath = savePath.replace("classes","INVOICE_PDF") + uuid ;

		logger.info("文件夹路径:{}", savePath);

		try {
			EInvoiceUtil.downLoadFromUrl(pdfUrl, fileName, savePath);
			return new File(savePath + fileName).exists() ? savePath + fileName : "";
		} catch (IOException e) {
			ExceptionUtil.throwBusiException(ErrorConstants.WX_INVOICE_PDFDOWNLOAD_FALIL);
			return "";
		}
    }

	/**
	 * 同步发票详情至本地
	 */
	public InvoiceDetail syncAndGetInvoiceDetail(String idCard, String areaCode, String type, String invoiceId, JSONObject invoiceInfo, JSONObject body) {
		try {
			// 发票详情dto
			InvoiceDetail detail;
			// 发票详情object
			JSONObject invoiceObject = this.getInvoiceDetailObject(areaCode, invoiceId);
			if (null == invoiceObject) {
				ExceptionUtil.throwBusiException(ErrorConstants.QX_SYNC_INVOICE_ERROR);
				return null;
			}
			String url = invoiceObject.getString("url");
			String invoiceDate = invoiceObject.getString("invoiceDate");
			String invoiceCode = invoiceObject.getString("invoiceCode");
			String invoiceNo = invoiceObject.getString("invoiceNo");
			String fakeCode = invoiceObject.getString("fakeCode");
			String amount = invoiceObject.getString("value");

			if (StringUtils.isEmpty(url) || StringUtils.isEmpty(invoiceDate) || StringUtils.isEmpty(invoiceCode) || StringUtils.isEmpty(invoiceNo) || StringUtils.isEmpty(fakeCode) || StringUtils.isEmpty(amount)) {
				return null;
			}
			String partyId = body.getString("partyId"); // 客户id
			// 订单id
			final String issueId = "oid_" + UUIDHelper.getUUID();
			// 详情id
			final String did = UUIDHelper.getUUID();
			// 发票主体信息
			InvoiceOrder order = this.setOrderByHis(issueId, areaCode, type, amount, body, invoiceInfo);
			// 发票详情信息
			List<InvoiceOrderDetail> invoiceList = new ArrayList<>();
			InvoiceOrderDetail invoice = this.setInvoiceByHis(issueId, did, url, invoiceDate, invoiceCode, invoiceNo, fakeCode, amount, invoiceId, type, partyId, invoiceInfo);
			invoiceList.add(invoice);
			// 用户信息
			User user = this.setUserByHis(idCard, partyId, areaCode, body, invoiceInfo);
			boolean isSuccess = this.invoiceService.commitInvoiceInfo(order, invoiceList, user, "开票历史同步开具成功发票");
			if (!isSuccess) {
				this.checkInfoMapper.deleteOrderByOrderId(issueId);
				this.checkInfoMapper.deleteOrderDetailByOrderId(issueId);
				logger.debug("MySql记录添加异常：{}", issueId);
				ExceptionUtil.throwBusiException(ErrorConstants.ORDER_INSERT_ERROR);
			}
			detail = this.checkInfoMapper.selectInvoiceType(did);
			return detail;
		} catch (Exception e) {
			logger.error("同步发票详情至本地异常{}", e.getMessage());
			return null;
		}
	}

	/**
	 * 调用企信接口获取发票详情
	 */
	private JSONObject getInvoiceDetailObject(String areaCode, String invoiceId) {

		JSONObject invoiceObject;
		try {

			EInvoiceUtil.setHeadMap(areaCode);
			String url = EInvoiceUtil.getJsRemoteUrl("invoiceJS", invoiceId, areaCode);
			logger.info("企信电子发票信息查询接口URL:{}", url);

			String res = HttpClientUtils.sendGet(url, EInvoiceUtil.getInvoiceHeadMap());
			logger.info("企信电子发票信息查询接口出参:{}", res);

			if (StringUtils.isEmpty(res)) {
				return null;
			}

			invoiceObject = JSON.parseObject(res);

			return invoiceObject;
		} catch (Exception e) {
			logger.error("企信接口查询发票详情异常! 发票实例id:{} Exception:{}", invoiceId, e.getMessage());
			return null;
		}
	}

	/**
	 * 通过历史记录同步发票信息
	 */
	private InvoiceOrder setOrderByHis(String issueId, String areaCode, String type, String money, JSONObject body, JSONObject invoiceInfo) {

		String loginAccNbr = body.getString("loginAccNbr"); // 登陆号码

		// 开票号码
		String phone = invoiceInfo.getString("consumerPhone");
		String memo = invoiceInfo.getString("memo");

		if (StringUtils.isEmpty(phone)) {

			// 开票号码可能为空，只有类型为1000 1100发票需要从备注截取
			if (StringUtils.equals("1000", type) || StringUtils.equals("1100", type)) {
				String phoneStr = memo.substring(memo.lastIndexOf("开票号码") + 5);
				phone = EInvoiceUtil.isNumeric(phoneStr) ? phoneStr : null;
			}
		}

		InvoiceOrder order = new InvoiceOrder();

		String titleType = body.getString("titleType"); // 抬头类型
		// String phoneType = body.getString("phoneType"); // 开票号码类型
		String titleName = invoiceInfo.getString("taxName"); // 抬头信息
		String mail = invoiceInfo.getString("smsEmail");    // 邮箱

		order.setOrderId(issueId);
		order.setLoginAccNbr(loginAccNbr);
		order.setPhone(phone);
		order.setType(type);
		order.setTitleType(titleType);
		order.setTitleName(titleName);
		order.setMoney(money);
		order.setMail(mail);
		order.setAreaCode(areaCode);

		// 政企 预留 2019-11-07
		if (StringUtils.equals("2", titleType)) {
			String taxNum = invoiceInfo.getString("taxCode");

			String companyAddr = invoiceInfo.getString("addr");
			String companyPhone = invoiceInfo.getString("tel");

			order.setTaxNum(taxNum);
			order.setCompanyAddr(companyAddr);
			order.setCompanyPhone(companyPhone);

		}

		order.setRemark(memo);
		order.setSource("QX");

		return order;

	}

	/**
	 * 通过历史记录同步发票详情信息
	 */
	private InvoiceOrderDetail setInvoiceByHis(String issueId, String did, String url, String invoiceDate, String invoiceCode, String invoiceNo, String fakeCode, String amount, String invoiceId, String type, String partyId, JSONObject invoiceInfo) {

		InvoiceOrderDetail invoice = new InvoiceOrderDetail();
		if (url.contains("pdfurl=")){
			url = url.substring(url.indexOf("pdfurl=") + 7);
		}
		Date date = DateHelper.formatToUTC(invoiceDate);

		invoice.setId(did);
		invoice.setOrderId(issueId);
		invoice.setMoney(amount);
		invoice.setInvoiceId(invoiceId);
		invoice.setStatus(InvoiceConstant.INVOICE_FINISH);
		invoice.setType(type);
		invoice.setPdfUrl(url);
		invoice.setPdfDownload(InvoiceConstant.PDF_EMPTY);
		invoice.setPartyId(partyId);
		invoice.setGmtCreate(date);
		invoice.setInvoiceDate(date);
		invoice.setCheckCode(fakeCode);
		invoice.setInvoiceCode(invoiceCode);
		invoice.setInvoiceNo(invoiceNo);

		// 三种发票类型唯一标识
		switch (type) {
			case "1000":
				invoice.setBillMonth(invoiceInfo.getString("billMonth"));
				break;
			case "1100":
				invoice.setBillId(invoiceInfo.getString("id"));
				String orderDate = DateHelper.changeDateFormate(invoiceInfo.getString("paymentdate"), DateHelper.DEFAULT_DATETIME_FORMAT_A, DateHelper.DEFAULT_DATE_TIME_FORMAT);
				invoice.setPaymentDate(orderDate);
				break;
			case "1200":
				invoice.setOrderNumber(invoiceInfo.getString("orderNumber"));
				invoice.setOrderDate(invoiceInfo.getString("orderDate"));
				break;
			default:
				break;
		}

		return invoice;

	}

	/**
	 * 通过历史详情同步/初始化用户信息
	 */
	private User setUserByHis(String idCard, String partyId, String areaCode, JSONObject body, JSONObject invoiceInfo) {

		User user = new User();

		String mail = invoiceInfo.getString("smsEmail");    // 邮箱
		String loginAccNbr = body.getString("loginAccNbr"); // 登陆号码
		String loginPhoneType = body.getString("loginPhoneType");   // 登陆号码类型
		String partyType = body.getString("titleType");  // 用户类型 1个人 2政企
		String taxNum = invoiceInfo.getString("taxCode");	// 纳税人识别号

		user.setPartyId(partyId);
		user.setIdCard(idCard);
		user.setLoginPhone(loginAccNbr);
		user.setPhoneType(loginPhoneType);
		user.setMail(mail);
		user.setAreaCode(areaCode);
		user.setPartyType(partyType);
		user.setTaxNum(taxNum);

		return user;
	}
	
	/**
	 * 根据类型获取入参查询列名
	 * @param queryType 参数
	 * @return 结果
	 */
	public String getQueryTypeColumn(String queryType){
		String queryTypeColumn = "";
		if(StringUtils.equals("1000", queryType)){
			queryTypeColumn = "billMonth";
		} else if (StringUtils.equals("1100", queryType)) {
			queryTypeColumn = "id";
		} else if (StringUtils.equals("1200", queryType)) {
			queryTypeColumn = "orderNumber";
		}
		return queryTypeColumn;
	}

	/**
	 * 根据类型获取查询列
	 */
	public String getColumnName(String queryType) {
		String columnName = "";
		if(StringUtils.equals("1000", queryType)){
			columnName = "bill_month";
		} else if (StringUtils.equals("1100", queryType)) {
			columnName = "bill_id";
		} else if (StringUtils.equals("1200", queryType)) {
			columnName = "order_number";
		}
		return columnName;
	}

	/**
	 * 根据不同发票类型获取查询的值
	 */
	public String getServiceVal(String type, JSONObject invoiceInfo) {
		String serviceVal = "";
		if (StringUtils.equals("1000", type)) {
			serviceVal = invoiceInfo.getString("billMonth");
		} else if (StringUtils.equals("1100", type)) {
			serviceVal = invoiceInfo.getString("id");
		} else if (StringUtils.equals("1200", type)) {
			serviceVal = invoiceInfo.getString("orderNumber");
		}
		return serviceVal;
	}

	
	
	public void insertOpertaionLog(String bizName, String tableName, String columnName, String oldValue, String newValue, String partyId) {

		ExecutorService executorService = Executors.newSingleThreadExecutor();
		LogOperation logOperation = new LogOperation();
		logOperation.setBizName(bizName);
		logOperation.setTableName(tableName);
		logOperation.setColumnName(columnName);
		logOperation.setOldValue(oldValue);
		logOperation.setNewValue(newValue);
		logOperation.setOptUser(partyId);
		LogOperationThread thread = new LogOperationThread(logOperation);
		executorService.submit(thread);
		executorService.shutdown();
	}




















}
